package com.guardchina.framework.web.plugins.exception;

import com.google.gson.Gson;
import com.guardchina.framework.util.constant.ResultStatus;
import com.guardchina.framework.util.exception.BaseException;
import com.guardchina.framework.util.exception.BusinessException;
import com.guardchina.framework.util.exception.SystemException;
import com.guardchina.framework.util.model.ResultHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.multipart.MaxUploadSizeExceededException;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 描述：默认的异常处理器
 *
 * @author lwb
 * @date 2019/3/15 14:20
 */
@RestControllerAdvice
@Slf4j
public class DefaultWebExceptionHandler {

    @Value("${spring.application.name}")
    private String instantId;

    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public ResultHolder handleMethodArgumentNotValidException(MethodArgumentNotValidException e){
        log.error(String.format("参数异常, uri【%s】", getRequest().getRequestURI()), e);

        BindingResult bindingResult = e.getBindingResult();
        return resolveErrorMessage(bindingResult);

    }

    @ExceptionHandler(value = BindException.class)
    public ResultHolder handleBindException(BindException e){
        log.error(String.format("参数异常, uri【%s】", getRequest().getRequestURI()), e);

        BindingResult bindingResult = e.getBindingResult();
        return resolveErrorMessage(bindingResult);
    }

    @ExceptionHandler(value = MissingServletRequestParameterException.class)
    public ResultHolder handleMissingServletRequestParameterException(MissingServletRequestParameterException e){
        log.error(String.format("参数异常, uri【%s】", getRequest().getRequestURI()), e);

        return ResultHolder.error(ResultStatus.PARAMETER_ERROR, "缺少参数：" + e.getParameterName(), instantId);
    }

    @ExceptionHandler(value = MethodArgumentTypeMismatchException.class)
    public ResultHolder<Void> handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e){
        log.error(String.format("参数异常, uri【%s】", getRequest().getRequestURI()), e);

        return ResultHolder.error(ResultStatus.PARAMETER_ERROR, "参数类型不正确：" + e.getName(), instantId);
    }

    @ExceptionHandler(value = ConstraintViolationException.class)
    public ResultHolder handleConstraintViolationException(ConstraintViolationException e){
        log.error(String.format("参数异常, uri【%s】", getRequest().getRequestURI()), e);

        Set<ConstraintViolation<?>> violationExceptions = e.getConstraintViolations();
        List<String> errors = violationExceptions.stream().map(this::resolveErrorMessage).collect(Collectors.toList());
        return ResultHolder.error(ResultStatus.PARAMETER_ERROR, new Gson().toJson(errors), instantId);
    }



    @ExceptionHandler(value = MaxUploadSizeExceededException.class)
    public ResultHolder handleConstraintViolationException(MaxUploadSizeExceededException e){
        log.error(String.format("文件超过限制大小, uri【%s】", getRequest().getRequestURI()), e);
        return ResultHolder.error(ResultStatus.BUSINESS_ERROR, "文件大小超过限制");
    }

    @ExceptionHandler(value = SystemException.class)
    public ResultHolder handleSystemException(SystemException e){
        log.error(String.format("服务异常, uri【%s】", getRequest().getRequestURI()), e);
        return ResultHolder.error(e.getError(), e.getShortMessage(), instantId);
    }

    @ExceptionHandler(value = BusinessException.class)
    public ResultHolder handleBusinessException(BusinessException e){
        log.error(String.format("message【%s】, uri【%s】", e.getMessage(), getRequest().getRequestURI()), e);
        return ResultHolder.error(e.getError(), e.getShortMessage(), instantId);
    }


    @ExceptionHandler(value = BaseException.class)
    public ResultHolder handleBaseException(BaseException e){
        log.error(String.format("服务异常, uri【%s】", getRequest().getRequestURI()), e);

        return ResultHolder.error(e.getError(), e.getShortMessage(), instantId);
    }

    @ExceptionHandler(value = Exception.class)
    public ResultHolder handleException(Exception e){
        log.error(String.format("系统异常, uri【%s】", getRequest().getRequestURI()), e);

        return ResultHolder.error(ResultStatus.SYSTEM_ERROR, "服务异常，请稍候重试", instantId);
    }


    private String resolveErrorMessage(FieldError fieldError){
        return fieldError.getField() + ":" + fieldError.getDefaultMessage();
    }

    private ResultHolder resolveErrorMessage(BindingResult bindingResult){
        List<String> errors = bindingResult.getFieldErrors().stream().map(this::resolveErrorMessage).collect(Collectors.toList());
        return ResultHolder.error(ResultStatus.PARAMETER_ERROR, new Gson().toJson(errors), instantId);
    }

    private String  resolveErrorMessage(ConstraintViolation resolvable){
        return resolvable.getMessage();
    }

    private HttpServletRequest getRequest(){
        return ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest();
    }

}
